package p2psvideo;

import android.annotation.SuppressLint;
import android.content.Context;
import android.media.AudioManager;
import android.media.MediaCodecInfo;
import android.media.MediaCodecList;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.Surface;

import java.lang.reflect.Method;
import java.util.LinkedList;
import java.util.List;

import p2pproxy.P2PSClient;
import p2pproxy.P2PSClient.DataItem;
import p2pproxy.P2PSManager;

@SuppressLint("NewApi")
public class P2PSMediaPlayer implements IAVPlayerCtl
{
	public static final int MSG_VIDEO_SIZE = 100;
	//缓冲中
	private static final int MSG_BUFFER_UPDATE = 101;
	public static final int MSG_AUDIO_INFO=102;
	//缓冲完成
	private static final int MSG_BUFFERR_FINISH = 103;
	private P2PSClient _client;
	private Thread _playThread;
	private Surface _surface;
	private volatile boolean _quit;
	private volatile boolean _paused;
	private volatile boolean _bufferring;
	private Handler _handler;
	private VideoTrackPlayer _vPlayer;
	private AudioTrackPlayer _aPlayer;
	private int _latency = 0;

	static class PlayThread extends Thread {
		private P2PSMediaPlayer _player;
		PlayThread(P2PSMediaPlayer player)
		{
			_player = player;
		}
		@Override
		public void run() {
			_player._doRun();
			_player = null;
			super.run();
			try {
				this.finalize();
			} catch (Throwable e) {
				e.printStackTrace();
			}
		}
	}
	public P2PSMediaPlayer(Context ctx, P2PSClient client, Surface v, Handler handler) {
		_client = client;
		_handler = handler;
		_surface = v;
		AudioManager am = (AudioManager)ctx.getSystemService(Context.AUDIO_SERVICE);
		try{
			Method m = am.getClass().getMethod("getOutputLatency", int.class);
			_latency = (Integer)m.invoke(am, AudioManager.STREAM_MUSIC);
			_latency *= 1000;
		}catch(Throwable e){
			_latency = P2PSManager.getAudioOutputLatency(AudioManager.STREAM_MUSIC);
			if (_latency != -1) _latency *= 1000;
			e.printStackTrace();
		}
		Log.v("P2PSMediaPlayer", "Latency: " + _latency);
	}
	public static boolean hasHardwareCodecForType(String mime)
	{
		if (android.os.Build.VERSION.SDK_INT < 16) return false;
		boolean newCodecs = android.os.Build.VERSION.SDK_INT >= 21;
		int n;
		MediaCodecList m = null;
		MediaCodecInfo[] infos=null;
		if (newCodecs) {
			m = new MediaCodecList(MediaCodecList.ALL_CODECS);
			infos = m.getCodecInfos();
			n = infos.length;
		} else {
			n = MediaCodecList.getCodecCount();
		}
		for(int i=0; i<n; ++i) {
			MediaCodecInfo info;
			if (newCodecs) {
				info = infos[i];
			} else {
				info = MediaCodecList.getCodecInfoAt(i);
			}
			if (info.isEncoder()) continue;
			String name=info.getName();
			if (name.startsWith("OMX.google.")) continue;

			String[] types = info.getSupportedTypes();
			if (types == null || types.length == 0) continue;
			for(int j=0; j<types.length; ++j) {
				if (types[j].equals(mime)) return true;
			}
		}
		return false;
	}

	public static String getSupportedVideoCodecList(boolean justHardware)
	{
		String r = "";
		if (android.os.Build.VERSION.SDK_INT < 16) return r;
		boolean newCodecs = android.os.Build.VERSION.SDK_INT >= 21;
		int n;
		MediaCodecList m = null;
		MediaCodecInfo[] infos=null;
		if (newCodecs) {
			m = new MediaCodecList(MediaCodecList.ALL_CODECS);
			infos = m.getCodecInfos();
			n = infos.length;
		} else {
			n = MediaCodecList.getCodecCount();
		}
		for(int i=0; i<n; ++i) {
			MediaCodecInfo info;
			if (newCodecs) {
				info = infos[i];
			} else {
				info = MediaCodecList.getCodecInfoAt(i);
			}
			if (info.isEncoder()) continue;
			Log.v("P2PSService", "codec name: " + info.getName());
			String[] types = info.getSupportedTypes();
			String typesStr="";
			for(int k=0; types != null && k<types.length; ++k) {
				typesStr += types[k];
				typesStr += ",";
			}
			String name=info.getName();
			if (justHardware && name.equals("OMX.google.hevc.decoder")) { //filter google's hevc SW decoder
				continue;
			}
			if (info.isEncoder()) {
				Log.v("P2PSService", "encoder types: " +  typesStr);
				continue;
			} else {
				Log.v("P2PSService", "decoder types: " +  typesStr);
			}

			if (types == null || types.length == 0) continue;
			for(int j=0; j<types.length; ++j) {
				if (types[j].startsWith("video/")) {
					if (types[j].endsWith("/avc")) {
						if (r.length() != 0) {
							r += ",";
						}
						r += "h264,avc ,avc1";
					} else if (types[j].endsWith("/hevc")) {
						if (r.length() != 0) {
							r += ",";
						}
						r += "h265,hevc";
					} else if (types[j].endsWith("/mp4v-es")) {
						if (r.length() != 0) {
							r += ",";
						}
						r += "mp4v";
					} else if (types[j].endsWith("/3gpp")) {
						if (r.length() != 0) {
							r += ",";
						}
						r += "h263 ";
					}
				}
			}
		}
		return r;
	}

	public int getWidth()
	{
		if (_vPlayer == null) return 0;
		return _vPlayer.getWidth();
	}
	public int getHeight()
	{
		if (_vPlayer == null) return 0;
		return _vPlayer.getHeight();
	}
	public void start()
	{
		stop();
		_quit = false;
		_paused = false;
		_bufferring = true;
		_aPlayer = new AudioTrackPlayer(this, _latency);
		_aPlayer.start();
		_vPlayer = new VideoTrackPlayer(this);
		_vPlayer.setSurface(_surface);
		_vPlayer.start();
		_playThread = new PlayThread(this);
		_playThread.start();
	}

	protected void _doRun() {
		List<DataItem> its = new LinkedList<DataItem>();
		boolean enoughData = false;
		int needDuration = -1;
		boolean eofSent=false;

		while (!_client.ready() && !_quit) { //等待数据传输ready
			try {
				Thread.sleep(5);
			} catch (InterruptedException e) {
				//e.printStackTrace();
			}
		}
		//数据已经ready
		while(!_quit) {
			if (!enoughData) { //缓冲数据不足，需要补充数据
				int preFillSize = its.size();
				int r = _client.getRaw(its, needDuration);
				boolean eof = its.isEmpty() && _client.stopped();
				//推入结束
				if (eof && !eofSent) {
					eofSent = true;
					_vPlayer.pushFrame(null);
					_aPlayer.pushFrame(null);
				}
				//缓冲中
				if (_bufferring && its.size() < 100 && !eof) {
					if (preFillSize != its.size()) {
						Message m = new Message();
						m.what = MSG_BUFFER_UPDATE;
						m.arg1 = its.size();
						_handler.sendMessage(m);
					}
					try {
						Thread.sleep(5);
					} catch (InterruptedException e) {
						//e.printStackTrace();
					}
					continue;
				}
			}

			//将得到的数据推入队列
			boolean sourceEmpty = its.isEmpty();
			while(!its.isEmpty()) {
				DataItem it = its.get(0);
				its.remove(0);
				deliverFrame(it);
			}

			//计算所需缓冲
			long bufLen = getBufferLength();
			if (bufLen >= 30000000) { //总缓冲足够30秒
				enoughData = true;
				needDuration = 1000;
			} else { //总缓冲不够30秒
				enoughData = false;
				needDuration = (int) ((30000000 - bufLen) / 1000);
				if (needDuration <= 0) {
					needDuration = 1000;
				}
			}

			if (_bufferring) { //结束缓冲
				_bufferring = false;
				_handler.sendEmptyMessage(MSG_BUFFERR_FINISH);
			} else {
				if (bufLen < 100 && !eofSent) { //缓冲区即将耗尽, 进入缓冲状态
					_bufferring = true;
					Message m = new Message();
					m.what = MSG_BUFFER_UPDATE;
					m.arg1 = 0;
					_handler.sendMessage(m);
					enoughData = false;
					continue;
				}
			}

			//如果数据足够或者读取不到数据，则休眠
			if ((sourceEmpty || enoughData) && !_quit) {
				try {
					if (enoughData) {
						Thread.sleep(1000);
					} else {
						Thread.sleep(5);
					}
				} catch (InterruptedException e) {
					//e.printStackTrace();
				}
			}
		}
	}

	public void setSurface(Surface f)
	{
		synchronized(this) {
			_surface = f;
			if (_vPlayer != null) _vPlayer.setSurface(f);
		}
	}

	public boolean bufferring()
	{
		return _bufferring;
	}

	public void pause()
	{
		if (_vPlayer != null) {
			_vPlayer.pause(true);
			_aPlayer.pause(true);
		}
		_paused = true;
	}

	public long getTime()
	{
		if (_vPlayer == null) return 0;
		long t1 = _vPlayer.getTime();
		long t2 = _aPlayer.getTime();
		if (t2 > t1) {
			t1 = t2;
		}
		if (t1 == -1) return 0;
		else return t1;
	}

	public long getBufferLength()
	{
		if (_vPlayer == null) return 0;
		long len = _vPlayer.getBufferLength();
		len *= 1000;
		long len2 = _aPlayer.getBufferLength();
		len2 *= 1000;
		if (len > len2) return len;
		else return len2;
	}

	public void resume()
	{
		_paused = false;
		if (_vPlayer != null) {
			_vPlayer.pause(false);
			_aPlayer.pause(false);
		} else {
			start();
		}
	}

	public boolean eof()
	{
		if (_vPlayer == null) return false;
		return _vPlayer.eof() && _aPlayer.eof();
	}

	public void stop()
	{
		_quit = true;
		if (_playThread != null) {
			_playThread.interrupt();
			try {
				_playThread.join();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			_playThread = null;
		}
		if (_vPlayer != null) {
			_vPlayer.close();
		}
		if (_aPlayer != null) {
			_aPlayer.close();
		}
		_vPlayer = null;
		_aPlayer = null;
		_bufferring = false;
	}

	@SuppressLint("NewApi")
	private void deliverFrame(DataItem it)
	{
		if (it.type == 0 || it.type == 2 || it.type == 4) {
			_vPlayer.pushFrame(it);
		} else  if (it.type == 1 || it.type == 3 || it.type == 5) {
			_aPlayer.pushFrame(it);
		}
		else {
			if (it.type == 8) { //fileTailer
				_aPlayer.pushFrame(null);
				_vPlayer.pushFrame(null);
			}
			_client.releaseDataItem(it);
		}
	}

	@Override
	public long getSyncTime() {
		return _aPlayer.getSoundPlayer().getTimeStamp();
	}

	@Override
	public boolean setSyncTime(long t) {
		return _aPlayer.getSoundPlayer().setTimeStamp(t);
	}

	@Override
	public void freeFrame(DataItem it) {
		_client.releaseDataItem(it);
	}

	@Override
	public void notifyVideoInfo(int width, int height, int sarNum, int sarDen) {
		_handler.sendEmptyMessage(MSG_VIDEO_SIZE);
	}

	@Override
	public void notifyAudioInfo(int sampleRate, int channels) {
		_handler.sendEmptyMessage(MSG_AUDIO_INFO);
	}

	@Override
	public void notifySyncTimeChanged() {
		_vPlayer.notifySyncTimeChanged();
	}
}